#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "socket_utils.h"

int g_num_child = 0;                        	  /* number of children (fork) */
#define LSOCKET_QUEUE 1024                  	  /* listen设置的缓冲区 */

//全局的会话结构体设置
dbsession_st *global_dbs = NULL; //主要用来处理会话中断时的会话关闭发送


#define GREPSERVERNUM   "ps aux|grep -w '%s'|grep -v 'grep'|grep -v 'gdb'|wc -l"
//检测服务 是否为唯一运行的程序
static int check_service_single(const char *server)
{
	FILE *ptr = NULL;
	char buff[256] = {0};

	/* 构造并执行命令 */
	snprintf(buff, 256, GREPSERVERNUM, server);
	ptr = popen(buff, "r");
	if (ptr == NULL) {
		//DEBUG(LOG_ERROR, "popen [%s] failed", buff);
		printf("popen [%s] failed\n", buff);
		return -1;
	}

	/* 检测服务个数 */
	if (fgets(buff, 256, ptr) != NULL) {
		if (atoi(buff) > 1) {
			//DEBUG(LOG_ERROR, "%s server already running, num %d invalid",server, atoi(buff));
            printf("%s server already running, num %d invalid\n",server, atoi(buff));
            pclose(ptr);
			return -1;
		}
	}

	pclose(ptr);
	return 0;
}

//销毁会话结构
void dbsession_destroy(dbsession_st *dbs)
{
	if (dbs == NULL) {
		return ;
	}
	global_dbs = NULL;

	//DEBUG(LOG_DEBUG, "dbsession_destroy");
	printf("dbsession_destroy\n");

	/* 释放插件私有数据 */
	if (dbs->pd_func != NULL && dbs->private != NULL) {
		dbs->pd_func(dbs->private);
	}

	/* 释放数据包结构 */
	dbs = NULL;
}

bool decode_dbproxy_port(char *buf, enum DBTYPE dbtype, int *proxy_port)
{
	if (NULL == buf){
		//DEBUG(LOG_ERROR,"input port info error");
		printf("input port info error\n");
		return false;
	}
	char proto[16] = {0};
	char port[16] = {0};
	sscanf(buf,"%[^:]:%[^\n]",proto,port);
	switch (dbtype) {
		case TYPE_MYSQL:
			if (strcmp(proto, "proxy_mysql") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_MSSQL:
			if (strcmp(proto, "proxy_mssql") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_DB2:
			if (strcmp(proto, "proxy_db2_v9") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_INFORMIX:
			if (strcmp(proto, "proxy_informix") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_SYBASE:
			if (strcmp(proto, "proxy_sybase") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_ORACLE:
			if (strcmp(proto, "proxy_oracle") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_POSTGRE:
			if (strcmp(proto, "proxy_pgsql") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_DM:
			if (strcmp(proto, "proxy_dm") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_KINGBASE:
			if (strcmp(proto, "proxy_kingbase") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_DB2_v10:
			if (strcmp(proto, "proxy_db2_v10") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_DM_UT8:
			if (strcmp(proto, "proxy_dm_utf8") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_MSSQL_v2008:
			if (strcmp(proto, "proxy_mssql_v08") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_GBASE_8S:
			if (strcmp(proto, "proxy_gbase_8s") == 0){
				*proxy_port = atoi(port);
			}
			break;
		case TYPE_GBASE_8A:
			if (strcmp(proto, "proxy_gbase_8a") == 0){
				*proxy_port = atoi(port);
			}
			break;
		default:
			*proxy_port = -1;
			//DEBUG(LOG_ERROR, "dbtype unknown, program exit");
			printf("dbtype unknown, program exit\n");
			return false;
	}
	return true;
}

//配置文件解析读取
bool load_proxy_port(enum DBTYPE dbtype, int *proxy_port)
{
    FILE *fp = NULL;
    char buf[32] = {0};

	fp = fopen(FILENAME_PROXY_PORT, "rb");
	if (!fp){
		//DEBUG(LOG_ERROR,"file %s fopen error",FILENAME_PROXY_PORT);
		printf("file %s fopen error\n",FILENAME_PROXY_PORT);
		return false;
	}

	while (fgets(buf, 32, fp)){
		if (decode_dbproxy_port(buf, dbtype, proxy_port) == false) {
			fclose(fp);
			return false;
		}
		if (*proxy_port > 0){
			break;
		}
	}
    fclose(fp);
	return true;
}

//获取对于协议代理端口
int proxy_port_get(enum DBTYPE dbtype)
{
	int proxy_port = 0;
	char cmd[256] = {'\0'};
	if(false == load_proxy_port(dbtype, &proxy_port)){
		proxy_port = -1;
		//DEBUG(LOG_ERROR, "dbtype unknown, program exit");
        printf("dbtype unknown, program exit\n");
	}
	if (proxy_port > 0) {
		/*检测端口号是否被占用，被占用则kill占用的进程*/
#define CHECK_PORT_KILL     "kill -9 `netstat -nltp |grep %d | awk '{print $7}'|awk -F '/' '{print $1}'` 2>/dev/null"
		snprintf(cmd, 256, CHECK_PORT_KILL, proxy_port);
		system(cmd);
	}

	return proxy_port;
}

//处理main函数的用户输入参数
static void process_arg(int argc, char **argv)
{
	int getopt_ret, option_index;
	static struct option long_options[] = {
		{"help",  optional_argument, 0, 'h'},
		{"daemon",  optional_argument, 0, 'd'},
		{"debug",  required_argument, 0, 'D'},
		{"level",  required_argument, 0, 'l'},
		{"dump_pkt",  no_argument, 0, 'k'},
	};

	int debug_flag = 0;
	char filename[128] = {'\0'};
	int debug_level = LOG_DEBUG;
	int enable_dpkt = 0;

	if (1 == argc) {
		//DEBUG(LOG_WARNING, "start without parameter");
		//debug_level_set("", debug_level, 0);
		printf("start without parameter\n");
		return ;
	}

	while (1) {
		getopt_ret = getopt_long( argc, argv, "hdkD:l:",
				long_options,  &option_index);
		if (getopt_ret == -1) break;

		switch(getopt_ret)
		{
			case 'h':
				//DEBUG(LOG_WARNING, USAGE, argv[0]);
				printf(USAGE, argv[0]);
				exit(0);
			case 'd':
				daemon(1, 0);
				//DEBUG(LOG_WARNING, "daemon ON,%s Will run in background mode !!!", argv[0]);
				printf("daemon ON,%s Will run in background mode !!!", argv[0]);
				break;
			case 'D':
				debug_flag = 1;
				if ((strstr(optarg, "NULL") != NULL) || (strstr(optarg, "null") != NULL)) {
					memset(filename, 0, 128);
				} else {
					snprintf(filename, 128, "%s", optarg);
				}
				break;
			case 'l':
				debug_flag = 1;
				debug_level = atoi(optarg);
				break;
			case 'k':
				enable_dpkt = 1;
				break;
			default:
				//DEBUG(LOG_WARNING, USAGE, argv[0]);
				printf(USAGE, argv[0]);
				exit(0);
		}
	}

#if 0    
	if (debug_flag) {
		debug_level_set(filename, debug_level, enable_dpkt);
	} else {
		debug_level_set(filename, LOG_OFF, enable_dpkt);	/* 没有接收的日志的文件描述符，就关闭日志输出 */
	}
#endif    
}

//设置监听的socketfd属性，ipv4/v6兼容
static int proxy_fdopt_set(struct addrinfo *res)
{
	int on = 1;
	char proxy_addr[64] = {0};               /* 代理客户端地址 */
	void *sinaddr = NULL;
	uint16_t sinport = 0;
	int sockclient = 0;                                 /* 代理服务器的socket fd */
	int reuse = 1;                                      /* 设置socket reuse属性 */
	struct sockaddr_in *addr = NULL;
	struct sockaddr_in6 *addr6 = NULL;

	if (res->ai_family == AF_INET){
		addr = (struct sockaddr_in *)res->ai_addr;
		sinaddr = (void *)&addr->sin_addr;
		sinport = addr->sin_port;
	} else {
		addr6 = (struct sockaddr_in6 *)res->ai_addr;
		sinaddr = (void *)&addr6->sin6_addr;
		sinport = addr6->sin6_port;
	}

	my_inet_ntop(res->ai_family, sinaddr, proxy_addr, 64);
	//DEBUG(LOG_INFO, " IP Address: %s", proxy_addr);
	//DEBUG(LOG_INFO, " Listening port: %d", ntohs(sinport));
    printf(" IP Address: %s\n", proxy_addr);
	printf(" Listening port: %d\n", ntohs(sinport));

	/* SOCKET CREATION */
	sockclient = socket_open(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (sockclient < 0) {
		//DEBUG(LOG_ERROR, "create socket failed");
		printf("create socket failed\n");
		return -1;
	}
	fcntl(sockclient, F_SETFD, FD_CLOEXEC);

	/* v6设置，只监听v6地址，避免绑定冲突 */
	if (res->ai_family == AF_INET6)
		setsockopt(sockclient, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));

	 //DEBUG(LOG_DEBUG, "**SOCKET created.");
	 printf("**SOCKET created.\n");

	/* SETSOCKOPT SO_REUSEADDR */
	if(setsockopt(sockclient, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
		//DEBUG(LOG_ERROR, "**Error on socket option.");
		printf("**Error on socket option.\n");
		socket_close(sockclient);
		return -1;
	}

	/* BINDING */
	if (socket_bind(sockclient, (struct sockaddr*)res->ai_addr, res->ai_addrlen)) {
		//DEBUG(LOG_ERROR, "bind proxy socket failed");
		printf("bind proxy socket failed\n");
		socket_close(sockclient);
		return -1;
	}
	//DEBUG(LOG_DEBUG, "**BIND success.");
    printf("**BIND success.\n");

	/* LISTEN */
	if (socket_listen(sockclient, LSOCKET_QUEUE) < 0) {
		//DEBUG(LOG_ERROR, "bind proxy socket failed");
		printf("bind proxy socket failed\n");
		socket_close(sockclient);
		return -1;
	}
	//DEBUG(LOG_DEBUG, "**LISTEN success.");
	printf"**LISTEN success.\n");
	return sockclient;
}

/**
 *  创建代理监听socket fd
 *  \param [in]  ip 监听的ip 包括ipv4和v6地址
 *  \param [in]  proxy_port 监听端口
 *  \return  int  创建成功的fd  或者 -1
 **/
static int proxy_fd_create(char *ip, int proxy_port)
{
	if (ip == NULL || proxy_port <=0 || proxy_port >= SOCKET_PORT_MAX) {
		//DEBUG(LOG_WARNING, "param invalid  port=%d", proxy_port);
        printf("param invalid  port=%d\n", proxy_port);
		return -1;
	}

	/* 将端口格式化为字符串 */
	char strport[STR_PORT_MAX] = {0};
	snprintf(strport, STR_PORT_MAX, "%u", proxy_port);

	/* 根据字符串地址获取addrinfo结构 */
	struct addrinfo *res = NULL;
	if (socket_addrinfo_get(&res, ip, strport) != 0) {
		//DEBUG(LOG_ERROR, "socket_addrinfo_get ip[%s] port[%s] failed", ip, strport);
		printf("socket_addrinfo_get ip[%s] port[%s] failed\n", ip, strport);
		return -1;
	}

	/* 设置socketfd属性 */
	int fd = proxy_fdopt_set(res);
	socket_addrinfo_free(res);
	res = NULL;

	return fd;
}

//信号处理函数设置
/* SIGCHLD HANDLER */
void sigchld_handler(int s)
{
	while (waitpid(-1, NULL, WNOHANG) > 0) {
		g_num_child--;
	}
}

/* SIGINT HANDLER*/
void sigint_handler(int s)
{
	//DEBUG(LOG_DEBUG, "recv signal sig_%d", s);
	printf("recv signal sig_%d\n", s);
	kill(0, s);
	while(waitpid(-1, NULL, WNOHANG) > 0);
	dbsession_destroy(global_dbs);
	exit(0);
}

void sighup_handle(int s)
{
	//DEBUG(LOG_DEBUG, "sighup_handle");
	printf("sighup_handle\n");
	dbsession_destroy(global_dbs);
	exit(0);
}

void exception_signal_handle(int s)
{
	//DEBUG(LOG_DEBUG, "sighup");
	printf("sighup\n");
	dbsession_destroy(global_dbs);
	exit(0);
}

static int proxy_sig_handler()
{
	struct sigaction sa_ggchild;                        /* 处理SIGCHLD信号 */
	struct sigaction sa_sig_intr;                       /* 处理SIGINT信号 */
	struct sigaction sa_sig_pipe;                       /* 处理SIGPIPE信号 */
	struct sigaction sa_sig_hup;                        /* 处理SIGHUP信号 */
	struct sigaction sa_sig_abrt;                        /* 处理SIGABRT信号 */
	struct sigaction sa_sig_segv;                        /* 处理SIGSEGV信号 */
	struct sigaction sa_sig_term;                        /* 处理SIGTERM信号 */
	struct sigaction sa_sig_ill;

	int ret = 0;
	ret |= sig_handle(&sa_ggchild, SIGCHLD, sigchld_handler, SA_RESTART);
	ret |= sig_handle(&sa_sig_intr, SIGINT, sigint_handler, 0);
	ret |= sig_handle(&sa_sig_pipe, SIGPIPE, SIG_IGN, 0);
	ret |= sig_handle(&sa_sig_hup, SIGHUP, sighup_handle, 0);
	ret |= sig_handle(&sa_sig_abrt, SIGABRT, sigint_handler, 0);
	// ret |= sig_handle(&sa_sig_segv, SIGSEGV, sigint_handler, 0);
	ret |= sig_handle(&sa_sig_term, SIGTERM, sigint_handler, 0);
	ret |= sig_handle(&sa_sig_ill, SIGILL, sigint_handler, 0);

	return ret;
}



//代理服务器器main函数主流程
int proxy_main_handler(int argc, char **argv, enum DBTYPE dbtype, proxy_handle_func pfunc)
{
	pid_t pid;
	struct sockaddr_in client4 = {0};                   /* 连接的客户端socket  ipv4 */
	struct sockaddr_in6 client6 = {0};                  /* 连接的客户端socket  ipv6 */
	socklen_t clientsize = 0;                           /* 客户端socket长度 */
	int sockclient4 = -1;                               /* 代理服务器的socket fd ipv4*/
	int sockclient6 = -1;                               /* 代理服务器的socket fd ipv6*/
	int sockclient_conn = -1;                           /* 代理服务器accept的 fd */
	int proxy_port = 0;                                 /* 代理监听的端口 */
	void *addr = NULL;                                  /* 存放client的源地址 */
	uint16_t sinport = 0;                               /* 存放client源端口 */
	char cliaddr[64] = {0};                             /* 客户端ip地址 */
	int slret = 0;                                      /* select函数返回值 */
	fd_set multp;                                       /* select多路复用 */
	int af_family = 0;                                  /* ip类型 */


	if (pfunc == NULL) {
		//DEBUG(LOG_ERROR, "pfunc == NULL! exit!");
		printf("pfunc == NULL! exit!\n");
		return -1;
	}

	/* 检测程序是否单例运行 */
	if (check_service_single(basename(argv[0])) != 0) {
		return -1;
	}

	/* 处理输入参数 */
	process_arg(argc, argv);
	/* 获取设置的代理监听端口 */
	proxy_port = proxy_port_get(dbtype);
	if (proxy_port < 0) {
		goto proxy_end;
	}

	/* 创建代理服务器监听socket ipv4 ipv6*/
	sockclient4 = proxy_fd_create("0.0.0.0", proxy_port);
	sockclient6 = proxy_fd_create("::", proxy_port);
	if (sockclient4 == -1 || sockclient6 == -1) {
		goto proxy_end;
	}

	/* 设置信号处理 */
	if (proxy_sig_handler()) {
		goto proxy_end;
	}

	/* NEVER-ENDED LOOP */
	while (1) {
		/* 设置多路复用集合 */
		FD_ZERO(&multp);
		FD_SET(sockclient4, &multp);
		FD_SET(sockclient6, &multp);

		//clear client address
		memset((void *)&client4, 0, sizeof(client4));
		memset((void *)&client6, 0, sizeof(client6));

		/* 调用select 获取accept的连接 */
		slret = my_select(FD_SETSIZE, &multp, NULL, NULL, NULL);
		if (slret == -1) {
			//DEBUG(LOG_WARNING, "my_select return ret = %d", slret);
			printf("my_select return ret = %d\n", slret);
			break;
		}

		if (FD_ISSET(sockclient4, &multp)) {
			af_family = AF_INET;
			clientsize = (socklen_t)sizeof(client4);
			sockclient_conn = socket_accept(sockclient4, (sa_st*)&client4, &clientsize);
			if (sockclient_conn == -1) {
				break;
			}

			/* 获取地址和端口用于打印 */
			addr = &client4.sin_addr;
			sinport = client4.sin_port;
		} else {
			af_family = AF_INET6;
			clientsize = (socklen_t)sizeof(client6);
			sockclient_conn = socket_accept(sockclient6, (sa_st*)&client6, &clientsize);
			if (sockclient_conn == -1) {
				continue;
			}

			/* 获取地址和端口用于打印 */
			addr = &client6.sin6_addr;
			sinport = client6.sin6_port;
		}

		//DEBUG(LOG_DEBUG, "**ACCEPT success.");
		printf("**ACCEPT success.\n");
		my_inet_ntop(af_family, addr, cliaddr, 64);

		/* 创建子进程 */
		pid = fork();
		if (pid < 0) {
			//DEBUG(LOG_ERROR, "**Error in FORK: %s.", strerror(errno));
			printf("**Error in FORK: %s.\n", strerror(errno));
			socket_close(sockclient_conn);
			continue;
		}
		if (pid == 0) {
			/* CHILD */
			if (socket_close(sockclient4) != 0 || socket_close(sockclient6) != 0) {
				socket_close(sockclient_conn);
				break;
			}

			/* 处理客户端连接 */
			assert(pfunc != NULL);
			pfunc(af_family, sockclient_conn);
			break;
		} else {
			/* FATHER */
			/* child num */
			g_num_child++;
			socket_close(sockclient_conn);
			/* debug info */
			/*DEBUG(LOG_DEBUG, "***CHILD created.***");
			DEBUG(LOG_DEBUG, "**Child thread id: %u", (uint32_t)pid);
			DEBUG(LOG_DEBUG, "Host connected: %s:%d", cliaddr, htons(sinport));
			DEBUG(LOG_DEBUG, "Children: %d", g_num_child);*/
            
            printf("***CHILD created.***\n");
			printf("**Child thread id: %u\n", (uint32_t)pid);
			printf("Host connected: %s:%d\n", cliaddr, htons(sinport));
			printf("Children: %d\n", g_num_child);
		}
	}

proxy_end:
	socket_close(sockclient4);
	socket_close(sockclient6);
	return 0;
}

